package com.ctrip.framework.apollo.adminservice.controller;

import com.ctrip.framework.apollo.biz.entity.GrayReleaseRule;
import com.ctrip.framework.apollo.biz.entity.Namespace;
import com.ctrip.framework.apollo.biz.message.MessageSender;
import com.ctrip.framework.apollo.biz.message.Topics;
import com.ctrip.framework.apollo.biz.service.NamespaceBranchService;
import com.ctrip.framework.apollo.biz.service.NamespaceService;
import com.ctrip.framework.apollo.biz.utils.ReleaseMessageKeyGenerator;
import com.ctrip.framework.apollo.common.constants.NamespaceBranchStatus;
import com.ctrip.framework.apollo.common.dto.GrayReleaseRuleDTO;
import com.ctrip.framework.apollo.common.dto.NamespaceDTO;
import com.ctrip.framework.apollo.common.exception.BadRequestException;
import com.ctrip.framework.apollo.common.utils.BeanUtils;
import com.ctrip.framework.apollo.common.utils.GrayReleaseRuleItemTransformer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

/**
 * Namespace 分支 Controller
 */
@RestController
public class NamespaceBranchController {

    @Autowired
    private MessageSender messageSender;
    @Autowired
    private NamespaceBranchService namespaceBranchService;
    @Autowired
    private NamespaceService namespaceService;

    // 创建 Namespace 分支
    @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches", method = RequestMethod.POST)
    public NamespaceDTO createBranch(@PathVariable String appId,
                                     @PathVariable String clusterName,
                                     @PathVariable String namespaceName,
                                     @RequestParam("operator") String operator) {
        // 校验 Namespace 是否存在
        checkNamespace(appId, clusterName, namespaceName);
        // 创建子 Namespace
        Namespace createdBranch = namespaceBranchService.createBranch(appId, clusterName, namespaceName, operator);
        return BeanUtils.transfrom(NamespaceDTO.class, createdBranch);
    }

    @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules",
            method = RequestMethod.GET)
    public GrayReleaseRuleDTO findBranchGrayRules(@PathVariable String appId,
                                                  @PathVariable String clusterName,
                                                  @PathVariable String namespaceName,
                                                  @PathVariable String branchName) {

        checkBranch(appId, clusterName, namespaceName, branchName);

        GrayReleaseRule rules = namespaceBranchService.findBranchGrayRules(appId, clusterName, namespaceName, branchName);
        if (rules == null) {
            return null;
        }
        GrayReleaseRuleDTO ruleDTO =
                new GrayReleaseRuleDTO(rules.getAppId(), rules.getClusterName(), rules.getNamespaceName(),
                        rules.getBranchName());

        ruleDTO.setReleaseId(rules.getReleaseId());

        ruleDTO.setRuleItems(GrayReleaseRuleItemTransformer.batchTransformFromJSON(rules.getRules()));

        return ruleDTO;
    }

    // 更新 Namespace 分支的灰度规则
    @Transactional
    @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}/rules", method = RequestMethod.PUT)
    public void updateBranchGrayRules(@PathVariable String appId, @PathVariable String clusterName,
                                      @PathVariable String namespaceName, @PathVariable String branchName,
                                      @RequestBody GrayReleaseRuleDTO newRuleDto) {
        // 校验子 Namespace
        checkBranch(appId, clusterName, namespaceName, branchName);

        // 将 GrayReleaseRuleDTO 转成 GrayReleaseRule 对象
        GrayReleaseRule newRules = BeanUtils.transfrom(GrayReleaseRule.class, newRuleDto);
        // JSON 化规则为字符串，并设置到 GrayReleaseRule 对象中
        newRules.setRules(GrayReleaseRuleItemTransformer.batchTransformToJSON(newRuleDto.getRuleItems()));
        // 设置 GrayReleaseRule 对象的 `branchStatus` 为 ACTIVE
        newRules.setBranchStatus(NamespaceBranchStatus.ACTIVE);
        // 更新子 Namespace 的灰度发布规则
        namespaceBranchService.updateBranchGrayRules(appId, clusterName, namespaceName, branchName, newRules);
        // 发送 Release 消息
        messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName), Topics.APOLLO_RELEASE_TOPIC);
    }

    @Transactional
    @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches/{branchName}", method = RequestMethod.DELETE)
    public void deleteBranch(@PathVariable String appId, @PathVariable String clusterName,
                             @PathVariable String namespaceName, @PathVariable String branchName,
                             @RequestParam("operator") String operator) {
        checkBranch(appId, clusterName, namespaceName, branchName);

        namespaceBranchService.deleteBranch(appId, clusterName, namespaceName, branchName, NamespaceBranchStatus.DELETED, operator);

        messageSender.sendMessage(ReleaseMessageKeyGenerator.generate(appId, clusterName, namespaceName), Topics.APOLLO_RELEASE_TOPIC);
    }

    @RequestMapping(value = "/apps/{appId}/clusters/{clusterName}/namespaces/{namespaceName}/branches", method = RequestMethod.GET)
    public NamespaceDTO loadNamespaceBranch(@PathVariable String appId, @PathVariable String clusterName,
                                            @PathVariable String namespaceName) {
        checkNamespace(appId, clusterName, namespaceName);

        Namespace childNamespace = namespaceBranchService.findBranch(appId, clusterName, namespaceName);
        if (childNamespace == null) {
            return null;
        }

        return BeanUtils.transfrom(NamespaceDTO.class, childNamespace);
    }

    // 校验子 Namespace 是否存在
    private void checkBranch(String appId, String clusterName, String namespaceName, String branchName) {
        // 校验 Namespace 是否存在
        // 1. check parent namespace
        checkNamespace(appId, clusterName, namespaceName);

        // 校验子 Namespace 是否存在。若不存在，抛出 BadRequestException 异常
        // 2. check child namespace
        Namespace childNamespace = namespaceService.findOne(appId, branchName, namespaceName);
        if (childNamespace == null) {
            throw new BadRequestException(String.format("Namespace's branch not exist. AppId = %s, ClusterName = %s, "
                            + "NamespaceName = %s, BranchName = %s", appId, clusterName, namespaceName, branchName));
        }
    }

    // 校验 Namespace 是否存在
    private void checkNamespace(String appId, String clusterName, String namespaceName) {
        // 查询父 Namespace 对象
        Namespace parentNamespace = namespaceService.findOne(appId, clusterName, namespaceName);
        // 若父 Namespace 不存在，抛出 BadRequestException 异常
        if (parentNamespace == null) {
            throw new BadRequestException(String.format("Namespace not exist. AppId = %s, ClusterName = %s, NamespaceName = %s",
                    appId, clusterName, namespaceName));
        }
    }

}
